﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Diagnostics;
using XMLGener.Utils;
using System.IO;
using XMLGener.Interfaces;

namespace XMLGener.Data
{
    public delegate void GeneratingStarted(XLogMessage message);


    /// <summary>
    /// This class represents XTemlpate document.
    /// It keeps all data about XTemplate framework.
    /// </summary>
    public class XMLGenerDocument:XMLGenerNode
    {
        #region Events
        /// <summary>
        /// Event, which is disposed, when generating of this document starts.
        /// </summary>
        public event GeneratingStarted startGeneratingEvent;

        #endregion

        #region initialization

        /// <summary>
        /// Default contstuctor
        /// </summary>
        public XMLGenerDocument()
        {
            createEmptyRootElement();
            this.settingsElementName = "settings";
        }

        /// <summary>
        /// Constructor with logger setting.
        /// </summary>
        /// <param name="xlogger"></param>
        public XMLGenerDocument(XLogger xlogger)
        {
            createEmptyRootElement();
            this._logger = xlogger;
            this.settingsElementName = "settings";
        }

        /// <summary>
        /// Set logger for loging. If logger is not set, than use
        /// default loger. Logger can be set in case, when logs 
        /// should be separted from other logs (eg. in generating, where
        /// logs should be shown in log list in generating form)
        /// </summary>
        public override XLogger logger
        {
            get
            {
                if (_logger == null)
                    return XLogger.instance;
                else
                    return _logger;
            }
        }

        #endregion
        #region Loading/creting new 

        /// <summary>
        /// Source document file
        /// </summary>
        private string _sourceFile = null;

        /// <summary>
        /// return source file of this document
        /// </summary>
        public string sourceFile
        {
            get { return _sourceFile; }
        }
        
        /// <summary>
        /// Private indicator, if document is new = no source file 
        /// for this document exists.
        /// </summary>
        private bool _isNew = true;

        /// <summary>
        /// This mode sets, if included files will be loaded
        /// from cache, or as new instances.
        /// </summary>
        protected bool editingMode = false;
        
        /// <summary>
        /// Indicator, if document is new (not loaded from file)
        /// </summary>
        public bool isNew
        {
            get { return _isNew; }
        }
        private static Dictionary<string,XMLGenerDocument> documentCopies = new Dictionary<string,XMLGenerDocument>();

        private static XMLGenerDocument openDocumentCopy(string fileName)
        {
            if (documentCopies.ContainsKey(fileName))
                return documentCopies[fileName];
            XMLGenerDocument newDocument = new XMLGenerDocument();
            newDocument.LoadFromFile(fileName);
            documentCopies.Add(fileName, newDocument);
            return newDocument;
        }

        private Dictionary<string, XMLGenerElement> allElementTypes= new Dictionary<string, XMLGenerElement>();

        public XMLGenerElement getElementType(string typeName)
        {
            if (string.IsNullOrEmpty(typeName))
                return null;
            if (this.allElementTypes.ContainsKey(typeName))
                return this.allElementTypes[typeName];
            XMLGenerElement result = null;
            XMLGenerElement elem=null;
            foreach(XMLGenerNode node in this.typesElement.childNodes)
            {
                if (!(node is XMLGenerElement))
                    continue;
                elem = node as XMLGenerElement;
                if (node == null)
                    continue;
                if (node.name == typeName)
                {
                    //result = node.getCopy() as XMLGenerElement;
                    if (node is XMLGenerElement)
                    {
                        result = node as XMLGenerElement;
                        allElementTypes.Add(typeName, result);
                    }
                    return result;
                }
            }
            foreach (XMLGenerDocument includedDocument in this.allIncludedDocuments)
            {
                foreach (XMLGenerElement node in includedDocument.typesElement.childNodes)
                {
                    if (!(node is XMLGenerElement))
                        continue;
                    elem = node as XMLGenerElement;
                    if (node == null)
                        continue;
                    if (node.name == typeName)
                    {
                        if (node is XMLGenerElement)
                        {
                            result = node as XMLGenerElement;
                            allElementTypes.Add(typeName, result);
                        }
                        return result;
                    }
                }
            }

            return result;
        }
        /// <summary>
        /// Return instance of document loaded from file
        /// </summary>
        /// <param name="fileName">source file</param>
        /// <returns>instance of document</returns>
        public static XMLGenerDocument loadDocumentForEdit(string fileName)
        {
            XMLGenerDocument newDocument = new XMLGenerDocument();
            newDocument.LoadFromFile(fileName);
            newDocument.editingMode = true;
            return newDocument;
        }
       

        /// <summary>
        /// Load XGenerateDocument from file.
        /// </summary>
        /// <param name="fileName"></param>
        public void LoadFromFile(String fileName)
        {
            xDoc = new XmlDocument();
            this.logger.addLog(this.GetType().ToString(), "Loading file:" + fileName);
            try
            {
                xDoc.Load(fileName);
            }
            catch (Exception e)
            {
                this.logger.addErrorLog(this.GetType().ToString(), "Loading document:" + e.Message);
                createEmptyRootElement();
                return;
            }
            this._sourceFile = fileName;
            XmlElement docElem = xDoc.DocumentElement;
            if (docElem == null || docElem.Name != "xdoc")
            {
                createEmptyRootElement();
                return;
            }
            //set that this document is loaded from file
            this._isNew = false;
            // parse document
            this.documentElement = docElem;
            this.xmlElem = docElem;
            XmlElement mainXMLElement = XMLUtils.getOrCreateSubElement(this.documentElement, XMLGenerNode.XMAINELEMENT_STR);
            this.mainElement = new XMLGenerMainElement(mainXMLElement);
            this.mainElement.ownerDocument = this;

            XmlElement typesXMLElement = XMLUtils.getOrCreateSubElement(this.documentElement, XMLGenerNode.XTYPES_STR);
            this.typesElement = new XMLGenerElementTypes(typesXMLElement);
            this.typesElement.ownerDocument = this;

            XmlElement dataTypesXMLElement = XMLUtils.getOrCreateSubElement(this.documentElement, XMLGenerNode.XDATATYPES_STR);
            this.dataTypesElement = new XMLGenerDataTypes(dataTypesXMLElement);
            this.dataTypesElement.ownerDocument = this;


            loadTreeFromXML();
            this.logger.addLog(this.GetType().ToString(), "Loading done." );
            
        }

        /// <summary>
        /// Create tree structure according to xml structure of document
        /// </summary>
        private void loadTreeFromXML()
        {
            Queue<XMLGenerNode> xque = new Queue<XMLGenerNode>();
            xque.Enqueue(this.mainElement);
            xque.Enqueue(this.typesElement);
            xque.Enqueue(this.dataTypesElement);
            XMLGenerNode node = null;
            XmlNode xmlNode = null;
            XmlNode xmlAttrNode = null;
            bool nodeIsDataTypes = false;
            bool nodeIsElementTypes = false;
            while (xque.Count > 0)
            {
                node = xque.Dequeue();
                if (node != null)
                    xmlNode = node.xmlElem;
                if (xmlNode != null) 
                    xmlNode = xmlNode.FirstChild;
                nodeIsDataTypes = (node!=null) && (node is XMLGenerDataTypes);
                nodeIsElementTypes = (node!=null) && (node is XMLGenerElementTypes);
                while (xmlNode != null)
                {
                    if (xmlNode.NodeType == XmlNodeType.Element)
                    {
                        if (xmlNode.Name == XMLGenerNode.XELEMENT_STR && !nodeIsDataTypes)
                        {
                            if (nodeIsElementTypes)
                                xque.Enqueue(new XMLGenerElementType(node, (XmlElement)xmlNode));
                            else
                                xque.Enqueue(new XMLGenerElement(node, (XmlElement)xmlNode));
                        }
                        else if (xmlNode.Name == XMLGenerNode.XELEMENTS_STR && !nodeIsDataTypes)
                        {
                            xque.Enqueue(new XMLGenerElements(node, (XmlElement)xmlNode));
                        }
                        else if (xmlNode.Name == XMLGenerNode.XTEXTNODE_STR && !nodeIsDataTypes)
                        {
                            xque.Enqueue(new XMLGenerTextNode(node, (XmlElement)xmlNode));
                        }
                        else if (xmlNode.Name == XMLGenerNode.XATTRIBUTES_STR && !nodeIsDataTypes)
                        {
                            xmlAttrNode = XMLUtils.getSubElement((XmlElement)xmlNode, XMLGenerNode.XATTRIBUTE_STR);
                            while (xmlAttrNode != null)
                            {
                                if (node is XMLGenerElement)
                                    new XMLGenerAttribute((XMLGenerElement)node, (XmlElement)xmlAttrNode);
                                else
                                    this.logger.addErrorLog(this.GetType().ToString(), "Loading document error: parent of attribute can be only element.");
                                xmlAttrNode = XMLUtils.getNextElement((XmlElement)xmlAttrNode, XMLGenerNode.XATTRIBUTE_STR); ;
                            }
                        }
                        else if (xmlNode.Name == XMLGenerNode.XDATATYPE_STR && nodeIsDataTypes)
                        {
                            if (node is XMLGenerDataTypes)
                            {
                                XDataType newDataType = new XDataType((XMLGenerDataTypes)node, (XmlElement)xmlNode);
                            }
                        }
                    }

                    xmlNode = xmlNode.NextSibling;
                }
                
            }

        }
        #endregion
        #region Saving
        /// <summary>
        /// Save xml document into source file
        /// </summary>
        public void save()
        {
            if (this.isNew)
                return;
            this.xDoc.Save(_sourceFile);
            if (_sourceFile!=null && documentCopies.ContainsKey(sourceFile))
            {
                XMLGenerDocument d =documentCopies[sourceFile];
                d.LoadFromFile(d.sourceFile);

            }
        }

        /// <summary>
        /// Saves this document into new xml file.
        /// </summary>
        /// <param name="fileName"></param>
        public void saveAs(string fileName)
        {
            try
            {
                changeDirectoriesOfIncludedFiles(fileName);
                this.xDoc.Save(fileName);
            }
            catch (XmlException e)
            {
                this.logger.addErrorLog(this.GetType().ToString(), "Saving document:" + e.Message);
                return;
            }
            this._isNew = false;
            this._sourceFile = fileName;
        }

        #endregion
        #region custom properties
        /// <summary>
        /// Return settings element of document. 
        /// </summary>
        public override XmlElement settingsElement
        {
            get { return base.getSettingElement(); }
        }
        

        /// <summary>
        /// Document element (xdoc)
        /// </summary>
        private XmlElement documentElement = null;

        /// <summary>
        /// Node that contains definitions of other element types
        /// </summary>
        private XMLGenerElementTypes typesElement = null;

        /// <summary>
        /// Node that contains definitions of other element types
        /// </summary>
        private XMLGenerDataTypes dataTypesElement = null;

        /// <summary>
        /// Root node of main tree structure.
        /// </summary>
        private XMLGenerMainElement mainElement = null;

        /// <summary>
        /// Return main element
        /// </summary>
        /// <returns></returns>
        public XMLGenerElement getMainElement() 
        {
            return mainElement;
        }

        /// <summary>
        /// Rerurn user defined types of elements
        /// </summary>
        /// <returns></returns>
        public XMLGenerElementTypes getTypesElement()
        {
            return typesElement;
        }

        /// <summary>
        /// Return user defined data types
        /// </summary>
        /// <returns></returns>
        public XMLGenerDataTypes getDataTypesElement()
        {
            return dataTypesElement;
        }


        /// <summary>
        /// XML file document, where this XMLGenerDocument is saved.
        /// </summary>
        private XmlDocument xDoc = null;

        #endregion

        #region file direcotry


        /// <summary>
        /// Source document file
        /// </summary>
        private string _sourceDirectory = null;
        public string sourceDirectory
        {
            get {
                if (string.IsNullOrEmpty(_sourceDirectory))
                    setDirectory();
                return _sourceDirectory;
            }
            set {
                _sourceDirectory = null;
                _sourceDirectoryInfo = null;
            }
        }

        private DirectoryInfo _sourceDirectoryInfo = null;

        public DirectoryInfo sourceDirectoryInfo
        {
            get {
                if (_sourceDirectoryInfo == null)
                {
                    _sourceDirectoryInfo = new DirectoryInfo(sourceDirectory);
                }
                return _sourceDirectoryInfo;
            }
        }

        private void setDirectory()
        { 
            if (string.IsNullOrEmpty(this.sourceFile))
            {
                _sourceDirectory = DirectoriesUtil.templatesDir.FullName;
                return;
            }
            FileInfo fi = new FileInfo(this.sourceFile);
            _sourceDirectory = fi.Directory.FullName;

        }

        private void changeDirectoriesOfIncludedFiles(string newFileName)
        {
            FileInfo fi = new FileInfo(newFileName);
            DirectoryInfo newDirectory = fi.Directory;

            foreach (TemplateFileInfo includedFile in this.includeFiles.Values)
            {
                includedFile.changeRelativePath(newDirectory);
            }
            foreach (TemplateFileInfo pregenerateFile in this.preGeneratedFiles.Values)
            {
                pregenerateFile.changeRelativePath(newDirectory);
            }

        }

        #endregion

        #region Creating

        /// <summary>
        /// Create new document
        /// </summary>
        public void createEmptyRootElement()
        {
            XSettings.loadSettings();
            this.xDoc = new XmlDocument();
            XmlElement docNode = xDoc.CreateElement("xdoc");
            xDoc.AppendChild(docNode);
            this.xmlElem = docNode;
            this.mainElement = new XMLGenerMainElement(this);
            this.mainElement.ownerDocument = this;

            this.typesElement = new XMLGenerElementTypes(this);
            this.typesElement.ownerDocument = this;

            this.dataTypesElement= new XMLGenerDataTypes(this);
            this.dataTypesElement.ownerDocument = this;

        }
        #endregion

        #region user defined data types/elements
        //----------------------
        // user defined types
        //-----------------------
        
        /// <summary>
        /// Function will return all valid data types. List contains all default
        /// data types concatenated with user defined data types.
        /// </summary>
        /// <returns>List of all data types</returns>
        public XDataTypeCollection getAllDataTypes() { return getAllDataTypes(null); }

        /// <summary>
        /// Function will return all valid data types. List contains all default
        /// data types concatenated with user defined data types. Result 
        /// will exclude node "ignoreNode"
        /// </summary>
        /// <param name="ignoreNode">node to be excluded from collection</param>
        /// <returns>List of all data types</returns>
        public XDataTypeCollection getAllDataTypes(XMLGenerNode ignoreNode)
        {
            XDataTypeCollection resultCollection = new XDataTypeCollection();
            resultCollection.AddRange(XMLGenerDataTypes.getDefaultTypes());
            resultCollection.AddRange(this.dataTypesElement.childNodes);
            foreach (XMLGenerDocument includedDocument in this.allIncludedDocuments)
            {
                resultCollection.AddRange(includedDocument.dataTypesElement.childNodes);
            }
            if (ignoreNode != null)
                resultCollection.Remove(ignoreNode);
            return resultCollection;
        }

        /// <summary>
        /// Return all user defined element definition to be inherited.
        /// </summary>
        /// <returns>List data types</returns>
        public XDataTypeCollection getAllParentTypes() { return this.getAllParentTypes(null); }
        
        /// <summary>
        /// Return all user defined element definition to be inherited. From result
        /// is excluded node "ignoreNode"
        /// </summary>
        /// <param name="ignoreNode">node to be excluded from collection</param>
        /// <returns>List of user defined elements</returns>
        public XDataTypeCollection getAllParentTypes(XMLGenerNode ignoreNode)
        {
            XDataTypeCollection resultCollection = new XDataTypeCollection();
            resultCollection.AddRange(this.typesElement.childNodes);
            foreach (XMLGenerDocument includedDocument in this.allIncludedDocuments)
            {
                resultCollection.AddRange(includedDocument.typesElement.childNodes);
            }

            if (ignoreNode!=null)
                resultCollection.Remove(ignoreNode);
            return resultCollection;
        }

        /// <summary>
        /// Return data type restriction. This method is used for
        /// setting module input settings.
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public string getRestictionForDataType(string name)
        {
            foreach (XDataType dType in this.getAllDataTypes())
            {
                if (dType.name == name)
                {
                    return dType.getRestrictionString();
                }
            }
            return "";
        }
        #endregion

        #region settings
        //----------------------
        // settings
        //-----------------------

        /// <summary>
        /// Return outId of document 
        /// </summary>
        public string outId
        {
            get 
            { 
                if (this.mainElement == null) 
                    return null;
                return this.mainElement.outId;
            } 

            set 
            {
                if (this.mainElement == null)
                    return;
                this.mainElement.outId = value;
            } 
        }

        private string _module = null;
        
        /// <summary>
        /// 
        /// </summary>
        public override string module
        {
            get {
                if (_module == null)
                {
                    _module= this.outputElem.GetAttribute("module");
                    if (_module == null || _module == "")
                        _module = "none";
                }
                return _module;
            }
            set {
                _module = value;
                this.outputElem.SetAttribute("module", value); 
            }
        }

        private string _secondModule = null;

        /// <summary>
        /// 
        /// </summary>
        public string secondModule
        {
            get
            {
                if (_secondModule == null)
                {
                    _secondModule = this.secondOutputElem.xmlElem.GetAttribute("module");
                    if (_secondModule == null || _secondModule == "")
                        _secondModule = "none";
                }
                return _secondModule;
            }
            set
            {
                _secondModule = value;
                this.secondOutputElem.xmlElem.SetAttribute("module", value);
            }
        }

        private XMLGenerNode _secondOutputElem = null;
        /// <summary>
        /// 
        /// </summary>
        public XMLGenerNode secondOutputElem
        {
            get 
            {
                if (_secondOutputElem == null)
                {
                    XmlElement secondOutput = XMLUtils.getOrCreateSubElement(settingsElement, "secondoutput");
                    _secondOutputElem = new XMLGenerNode(this,secondOutput);

                }
                return _secondOutputElem;
            }
        }

        private XmlElement _outputElem = null;

        /// <summary>
        /// Output setttins element (in template)
        /// </summary>
        public XmlElement outputElem
        {
            get 
            {
                if (_outputElem == null)
                {
                    _outputElem = XMLUtils.getSubElement(this.settingsElement, "output");
                    if (_outputElem == null)
                    {
                        _outputElem = XMLUtils.createSubElement(settingsElement, "output");
                    }
                }
                return _outputElem;
            }
        }

        private XmlElement _outputSettingElement = null;
        private XmlElement outputSettingElement
        {
            get
            {
                if (_outputSettingElement == null)
                {
                    _outputSettingElement = XMLUtils.getSubElement(this.outputElem, "setting");
                    if (_outputSettingElement == null)
                    {
                        _outputSettingElement = XMLUtils.createSubElement(this.outputElem, "setting");
                    }
                }
                return _outputSettingElement;
            }
        }


      
        /// <summary>
        /// Set value into setting of name "name"
        /// </summary>
        /// <param name="name">name of setting</param>
        /// <param name="value">value</param>
        public override void setSetting(string name,string value)
        {
            if (outputSettingElement == null)
            {
                logger.addErrorLog(this.name, "Set settings "+name+" failed ("+settingsElementName+" is null).");
                return;
            }

            outputSettingElement.SetAttribute(name, value);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public override string getSetting(string name)
        {
            XmlElement settElem = this.outputSettingElement;
            if (!settElem.HasAttribute(name))
            {
                return null;
            }
            return settElem.GetAttribute(name);
        }
        
        /// <summary>
        /// get/set output file
        /// </summary>
        public string outSchemeType
        {
            get { return this.outputElem.GetAttribute("schemeType"); }
            set { this.outputElem.SetAttribute("schemeType", value); }
        }

        /// <summary>
        /// get/set output file
        /// </summary>
        public string outFile
        {
            get { return this.outputElem.GetAttribute("outfile"); }
            set { this.outputElem.SetAttribute("outfile",value);}
        }
        /// <summary>
        /// Return full path of output file
        /// </summary>
        public string outFileFullPath
        {
            get { return
                DirectoriesUtil.combinePath(DirectoriesUtil.currentDir.FullName, this.outFile); 
            }
                    
        }
        /// <summary>
        /// Return fill path of file, where index is included.
        /// </summary>
        /// <param name="fileIndex"></param>
        /// <returns></returns>
        public string outFileFullPathIndex(int fileIndex)
        {
            return outFileFullPathIndex(fileIndex,true);
        }

        /// <summary>
        /// Get string, which will be composed of files name, index and indiciation, 
        /// if this file is/not temporary.
        /// </summary>
        /// <param name="fileIndex">Index of output file</param>
        /// <param name="isTemporaryFile">indication, if file is temporary</param>
        /// <returns></returns>
        public string outFileFullPathIndex(int fileIndex,bool isTemporaryFile)
        {
            if (this.module == "none")
                isTemporaryFile = false;
            if (this.documentsCount <= 1 && !isTemporaryFile)
                return outFileFullPath;
            FileInfo fi = new FileInfo( this.outFileFullPath);
            String schName = fi.Name;
            int docCount = this.documentsCount;
            String strDigits = "";
            
            while (docCount > 0)
            {
                strDigits+="0";
                docCount/=10;
            }
            StringBuilder sb = new StringBuilder();
            sb.Append("{0}");

            if (this.documentsCount>1)
                    sb.Append("{1:" + strDigits + "}");

            if (isTemporaryFile)
                sb.Append("_tmp");
            sb.Append("{2}");

            String shortFileName = schName.Replace(fi.Extension, "");
            shortFileName = String.Format(sb.ToString(), shortFileName, fileIndex+1, fi.Extension);
            schName = DirectoriesUtil.combinePath(fi.DirectoryName, shortFileName);
            return schName;
        }

        /// <summary>
        /// Random generator initializing seed 
        /// </summary>
        public int randomInit
        {
            get { return XMLUtils.getIntAttribute(this.settingsElement, "randominit"); }
            set { this.settingsElement.SetAttribute("randominit", Convert.ToString(value)); }
        }

        private int _docCount = 0;
        /// <summary>
        /// Random generator initializing seed 
        /// </summary>
        public int documentsCount
        {
            get { 
                if (_docCount<1)
                    _docCount = XMLUtils.getIntAttribute(this.outputElem, "doccount");
                _docCount = (1 > _docCount) ? 1 : _docCount;
                _docCount = (100 < _docCount) ? 100 : _docCount;
                return _docCount;
            }
            set {
                _docCount = value;
                this.outputElem.SetAttribute("doccount", Convert.ToString(value)); 
            }
        }

        #endregion

        #region included templates

        //--------------------------
        //other included templates
        //--------------------------
        
        private void refreshIncludedFilesCache()
        { 
            if (_allIncludedDocments!=null)
            {
                _allIncludedDocments.Clear();
                _allIncludedDocments = null;
            }
        }


        /// <summary>
        /// included files for this template
        /// </summary>
        private Dictionary<string, TemplateFileInfo> _includeFiles = null;

        private Dictionary<string, TemplateFileInfo> includeFiles
        {
            get 
            {
                if (_includeFiles == null)
                    loadIncludedFiles();
                return _includeFiles;
            }
        }

        /// <summary>
        /// Return array of included files.
        /// </summary>
        /// <returns></returns>
        public TemplateFileInfo[] getIncludeFiles()
        {
            return includeFiles.Values.ToArray();
        }


        private void loadIncludedFiles()
        {
            refreshIncludedFilesCache();
            _includeFiles = new Dictionary<string, TemplateFileInfo>();
            XmlElement includedFiles = XMLUtils.getSubElement(this.settingsElement,"include");

            XmlElement incl = XMLUtils.getSubElement(includedFiles, "incl");
            XmlElement removeElement = null;
            while (incl != null)
            {
                removeElement = incl;
                string filename = incl.GetAttribute("file");
                if (!string.IsNullOrEmpty(filename))
                {
                    TemplateFileInfo newInclude = new TemplateFileInfo(filename,this.sourceDirectory, incl);
                    if (!_includeFiles.ContainsKey(newInclude.absolutePath))
                    {
                        _includeFiles.Add(newInclude.absolutePath, newInclude);
                        removeElement = null;
                    }

                }
                incl = XMLUtils.getNextElement(incl,"incl");
                if (removeElement != null)
                    includedFiles.RemoveChild(removeElement);

            }

            
        }
        /// <summary>
        /// Add included files.
        /// </summary>
        /// <param name="name">relative/absoute path of file</param>
        /// <returns>true, if file was add</returns>
        public bool addIncludeFile(string name)
        {
            XmlElement incl = XMLUtils.getSubElement(this.settingsElement, "include");
            if (incl == null)
            {
                incl = XMLUtils.createSubElement(this.settingsElement,"include");
            }

            XmlElement newIncFile = XMLUtils.createSubElement(incl, "incl");
            newIncFile.SetAttribute("file", name);
            TemplateFileInfo newFile = new TemplateFileInfo(name, this.sourceDirectory,newIncFile);
            if (!includeFiles.ContainsKey(newFile.absolutePath))
            {
                includeFiles.Add(newFile.absolutePath, newFile);
                refreshIncludedFilesCache();
            }
            else
            {
                incl.RemoveChild(newIncFile);
                return false;
            }
            return true;
        }

        /// <summary>
        /// Remove file from list of included files.
        /// </summary>
        /// <param name="name">name of file</param>
        public void removeIncludeFile(TemplateFileInfo file )
        {
            if (file==null || file.sourceElement==null)
                return;
            XmlElement incl = XMLUtils.getSubElement(this.settingsElement, "include");
            if (incl == null)
            {
                return;
            }
            incl.RemoveChild(file.sourceElement);
            includeFiles.Remove(file.absolutePath);
            refreshIncludedFilesCache();
        }

        private List<XMLGenerDocument> _allIncludedDocments = null;
        
        private List<XMLGenerDocument> allIncludedDocuments 
        {
            get {
                if (_allIncludedDocments == null)
                    getAllIncludedDocuments();
                return _allIncludedDocments;
            }
        }

        private void getAllIncludedDocuments()
        {
            Stack<XMLGenerDocument> stack = new Stack<XMLGenerDocument>();
            stack.Push(this);
            
            Dictionary<string, XMLGenerDocument> includedDocuments = new Dictionary<string, XMLGenerDocument>();
            if(!this.isNew)
                includedDocuments.Add(this.sourceFile,this);
            XMLGenerDocument inclDoc = null;
            while (stack.Count > 0)
            {
                inclDoc = stack.Pop();
                if (inclDoc==null)
                    continue;
                foreach (TemplateFileInfo incFile in inclDoc.includeFiles.Values)
                {
                    if (incFile.fileExists && !includedDocuments.ContainsKey(incFile.absolutePath))
                    {
                        if (editingMode)
                        {
                            inclDoc = XMLGenerDocument.openDocumentCopy(incFile.absolutePath);
                        }
                        else
                        {
                            inclDoc = new XMLGenerDocument(this.logger);
                            inclDoc.LoadFromFile(incFile.absolutePath);
                        }
                        includedDocuments.Add(incFile.absolutePath, inclDoc);
                        stack.Push(inclDoc);
                    }
                }
            }
            if(!this.isNew)
                includedDocuments.Remove(this.sourceFile);
            _allIncludedDocments = new List<XMLGenerDocument>();
            _allIncludedDocments.AddRange(includedDocuments.Values);

        }

        #endregion
        

        #region pre-generated documents
        //--------------------------
        // pre-generated documents
        //--------------------------

        private Dictionary<string,TemplateFileInfo> _preGeneratedFiles =null;

        /// <summary>
        /// List of files, that should be generated before generating this document.
        /// </summary>
        public Dictionary<string, TemplateFileInfo> preGeneratedFiles
        {
            get
            {
                if (_preGeneratedFiles == null)
                    loadPreGeneratedFiles();
                return _preGeneratedFiles;
            }

        }

        private void loadPreGeneratedFiles()
        {
            _preGeneratedFiles = new Dictionary<string, TemplateFileInfo>();
            XmlElement pregenerate = XMLUtils.getSubElement(this.settingsElement, "pregenerate");
            if (pregenerate == null)
            {
                return;
            }

            XmlElement item = XMLUtils.getSubElement(pregenerate, "doc");
            XmlElement removeItem = null;
            while (item != null)
            {
                removeItem=item;
                string fileName = item.GetAttribute("file");
                if (!string.IsNullOrEmpty(fileName))
                {
                    TemplateFileInfo newFile = new TemplateFileInfo(fileName, this.sourceDirectory, item);
                    if (!_preGeneratedFiles.ContainsKey(newFile.absolutePath))
                    {
                        _preGeneratedFiles.Add(newFile.absolutePath, newFile);
                        removeItem = null;
                    }
                }
                item = XMLUtils.getNextElement(item, "doc");
                if (removeItem != null)
                    pregenerate.RemoveChild(removeItem);
            }
        }
        /// <summary>
        /// Return array of pre-generated files.
        /// </summary>
        /// <returns></returns>
        public TemplateFileInfo[] getPreGeneratedFiles()
        {
            return preGeneratedFiles.Values.ToArray();
        }

        private void refreshPregeneratedCash()
        {
            if (_preGeneratedIds != null)
            {
                _preGeneratedIds.Clear();
                _preGeneratedIds = null;
            }
            if (_preGeneratedDocuments != null)
            {
                _preGeneratedDocuments.Clear();
                _preGeneratedDocuments = null;
            }
            if (_preGeneratedDocumentsDict != null)
            {
                _preGeneratedDocumentsDict.Clear();
                _preGeneratedDocumentsDict = null;
            }
            if (_allRecursiveDocuments != null)
            {
                _allRecursiveDocuments.Clear();
                _allRecursiveDocuments = null;

            }

        }

        private List<string> _preGeneratedIds = null;
        
        private Dictionary<string, XMLGenerDocument> _preGeneratedDocumentsDict = null;
        private List<XMLGenerDocument> _preGeneratedDocuments = null;
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public List<string> getPreGeneratedIds()
        {
            if (_preGeneratedIds == null)
            {
                _preGeneratedIds= new List<string>();
                getPreGeneratedDocuments();
                _preGeneratedIds.AddRange(_preGeneratedDocumentsDict.Keys);
               
            }
            return _preGeneratedIds;

        }

        private List<XMLGenerDocument> getPreGeneratedDocuments()
        {
            if (_preGeneratedDocuments == null)
            {
                _preGeneratedDocuments = new List<XMLGenerDocument>();
                _preGeneratedDocumentsDict= new Dictionary<string, XMLGenerDocument>();
                TemplateFileInfo [] list = getPreGeneratedFiles();
                if (list!=null)
                    foreach (TemplateFileInfo shortFile in list)
                    {
                        XMLGenerDocument newDoc = null;
                        if (editingMode)
                        {
                            newDoc = XMLGenerDocument.openDocumentCopy(shortFile.absolutePath);
                        }
                        else {
                            newDoc = new XMLGenerDocument(this.logger);
                            newDoc.LoadFromFile(shortFile.absolutePath);
                        }

                        string outId = newDoc.outId;
                        _preGeneratedDocuments.Add(newDoc);
                        if (outId != null && outId.Length > 0 && !_preGeneratedDocumentsDict.ContainsKey(outId))
                        {
                            _preGeneratedDocumentsDict.Add(outId, newDoc);
                        }

                        
                    }

            }
            return _preGeneratedDocuments;
        }

        private Dictionary<string, XMLGenerDocument> _allRecursiveDocuments = null;
        private Dictionary<string, XMLGenerDocument> getAllRecursivePregeneratedDocuments()
        {
            if (_allRecursiveDocuments != null)
                return _allRecursiveDocuments;
            Stack<XMLGenerDocument> stack = new Stack<XMLGenerDocument>();
            stack.Push(this);
            XMLGenerDocument preDoc = null;
            String sourceFile = null;
            string fullFilePath = null ;
            _allRecursiveDocuments = new Dictionary<string, XMLGenerDocument>();
            _allRecursiveDocuments.Add(this.sourceFile, this);
            while(stack.Count >0)
            {
                preDoc = stack.Pop();
                if (preDoc == null)
                    continue;
                sourceFile = preDoc.sourceFile;
                if (sourceFile == null)
                    continue;
                if (preDoc.getPreGeneratedFiles() == null)
                    continue;
                foreach (TemplateFileInfo fileName in  preDoc.getPreGeneratedFiles())
                {
                    fullFilePath = fileName.absolutePath;
                    if (_allRecursiveDocuments.ContainsKey(fullFilePath))
                    {
                        if (_allRecursiveDocuments[fullFilePath].generateDependenyDepth < preDoc.generateDependenyDepth + 1)
                            _allRecursiveDocuments[fullFilePath].generateDependenyDepth = preDoc.generateDependenyDepth + 1;
                        continue;
                    }
                    XMLGenerDocument newDoc = null;
                    if (editingMode)
                    {
                        newDoc = XMLGenerDocument.openDocumentCopy(fullFilePath);
                    }
                    else
                    {
                        newDoc = new XMLGenerDocument(this.logger);
                        newDoc.LoadFromFile(fullFilePath);
                    }

                    newDoc.LoadFromFile(fullFilePath);
                    newDoc.generateDependenyDepth = preDoc.generateDependenyDepth + 1;
                    stack.Push(newDoc);
                    _allRecursiveDocuments.Add(fullFilePath, newDoc);
                }
               
            }
            if (sortedPreGeneratingDocuments == null)
                sortedPreGeneratingDocuments = new List<XMLGenerDocument>();
            sortedPreGeneratingDocuments.AddRange(_allRecursiveDocuments.Values);
            sortedPreGeneratingDocuments.Sort(docDependencyComparer);
            return _allRecursiveDocuments;
        
        }

        private List<XMLGenerDocument> sortedPreGeneratingDocuments = null;
        /// <summary>
        /// 
        /// </summary>
        public int generateDependenyDepth = 0;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="outId"></param>
        /// <returns></returns>
        public string getPathToGeneratedFile(string outId)
        {
            // initialize dictionary
            getPreGeneratedDocuments();

            if (!_preGeneratedDocumentsDict.ContainsKey(outId))
                return null;
            XMLGenerDocument genDoc = _preGeneratedDocumentsDict[outId];
            return DirectoriesUtil.combinePath(DirectoriesUtil.outputDir.FullName,genDoc.outFileFullPathIndex(0, false));
            
        }



        /// <summary>
        /// Add pre-generated file.
        /// </summary>
        /// <param name="name">relative/absoute path of file</param>
        /// <returns>true, if file was add</returns>
        public bool addPreGeneratedFiles(string name)
        {
            XmlElement pregen = XMLUtils.getSubElement(this.settingsElement, "pregenerate");
            if (pregen == null)
            {
                pregen = XMLUtils.createSubElement(this.settingsElement, "pregenerate");
            }

            XmlElement newElement = XMLUtils.createSubElement(pregen, "doc");
            newElement.SetAttribute("file", name);
            TemplateFileInfo newPregenItem = new TemplateFileInfo(name, this.sourceDirectory, newElement);
            if (preGeneratedFiles.ContainsKey(newPregenItem.absolutePath))
            {
                pregen.RemoveChild(newElement);
                return false;
            }
            preGeneratedFiles.Add(newPregenItem.absolutePath, newPregenItem);

            refreshPregeneratedCash();
            return true;
        }

        /// <summary>
        /// Remove file from list of pre-generated files.
        /// </summary>
        /// <param name="name">name of file</param>
        public void removePreGeneratedFiles(TemplateFileInfo file)
        {
            XmlElement pregen = XMLUtils.getSubElement(this.settingsElement, "pregenerate");
            if (pregen == null)
            {
                return;
            }
           
            if (preGeneratedFiles.ContainsKey(file.absolutePath))
            {
                pregen.RemoveChild(file.sourceElement);
                preGeneratedFiles.Remove(file.absolutePath);
            }
            refreshPregeneratedCash();
            return;
        }


        #endregion

        
        //--------------------------
        // Generating
        //--------------------------
        
        #region Caliculating


        /// <summary>
        /// 
        /// </summary>
        public decimal totalSum = 1;

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public bool preCalculate()
        {
         
            checkRecursivity();

            XMLGenerRangeElement el = this.mainElement;
            Stack<XMLGenerRangeElement> stack = new Stack<XMLGenerRangeElement>();
            stack.Push(el);
            // get min/max of child nodes
            if (!preCalculateMinMax(stack))
                return false;

            //calculate min/max total
            el = mainElement;
            stack.Push(el);
            if (!preCalculateMinMaxTotal(stack))
                return false;
           
       
            return true;
        }
        private void checkRecursivity()
        {
            Stack<XMLGenerRangeElement> stack = new Stack<XMLGenerRangeElement>();
            XMLGenerRangeElement el  = this.mainElement;
            stack.Push(el);
            XMLGenerElement elem;
            Dictionary<string,XMLGenerElementType> typeElements= new Dictionary<string,XMLGenerElementType>();
            while (stack.Count > 0)
            {
                el = stack.Pop();
                if (el!=null && el is XMLGenerElement)
                {
                    elem = el as XMLGenerElement;
                    if (elem.baseType != null && elem.baseType is XMLGenerElementType &&
                        elem.parentTypeName!=null && !typeElements.ContainsKey(elem.parentTypeName) )
                        typeElements.Add(elem.parentTypeName, elem.baseType as XMLGenerElementType);
                }
                elem = el as XMLGenerElement;
                foreach (XMLGenerNode childNode in el.childNodes)
                {
                    if (childNode is XMLGenerRangeElement)
                        stack.Push(childNode as XMLGenerRangeElement);
                };
            }
            
            foreach (XMLGenerNode node in this.typesElement.childNodes)
            {
                if (node!=null && node is XMLGenerElementType)
                {
                    XMLGenerElementType eltype = node as XMLGenerElementType;
                    if (eltype.name != null && !typeElements.ContainsKey(eltype.name))
                    {
                        typeElements.Add(eltype.name, eltype);
                    }
                }
            }
            if (typeElements.Count > 0)
            {
                foreach (XMLGenerElementType elType in typeElements.Values)
                    checkIfElementTypeIsRecursive(elType);
            }

        }

        private bool checkIfElementTypeIsRecursive(XMLGenerElementType elemType)
        {
            if (elemType==null)
                return true;
            Stack<XRecursionCheck> stack = new Stack<XRecursionCheck>();
            XRecursionCheck rootNode = new XRecursionCheck();
            rootNode.rootPath.Push(elemType);
            rootNode.rootPathInheritedTypes.Push(elemType);
            rootNode.childsLeft.Push(elemType);
            stack.Push(rootNode);
            //if (!preCalculateMinMax(stack))
              //  return false;
            return true;
            while (stack.Count > 0)
            { 
                XRecursionCheck checkNode = stack.Peek();
                if (checkNode == null)
                {
                    stack.Pop();
                    continue;
                }
                if (checkNode.childsLeft.Count==0)
                {
                    stack.Pop();
                }else{
                    
                    
                    if (checkNode.curentElement==null)
                    {
                        checkNode.curentElement = checkNode.childsLeft.Peek();
                    }



                }
            }

          //  stack.Push(elemType);
            //if (!preCalculateMinMaxTotal(stack))
              //  return false;
            
            return true;
        }
        
        private bool preCalculateMinMaxTotal(Stack<XMLGenerRangeElement> stack)
        {
            XMLGenerRangeElement el = this.mainElement;
            try
            {
                while (stack.Count > 0)
                {
                    el = stack.Peek();
                    if (el.childsTotalCalculated)
                    {
                        calculateTotalCount(el);
                        stack.Pop();
                    }
                    else
                    {
                        el.childsTotalCalculated = true;
                        foreach (XMLGenerRangeElement chNode in el.childNodes)
                        {
                            stack.Push(chNode);
                        }
                    }

                }
            }
            catch (OverflowException)
            {
                this.logger.addErrorLog(this.GetType().ToString(), "Stack overflow: number has more than 28 digits.");
                logger.addErrorHintLog(this.GetType().ToString(), "Decrease maximal count or fanout in some elements.");
                return false;
            }
            return true;
        }
        private bool preCalculateMinMax(Stack<XMLGenerRangeElement> stack)
        {
            try{

                XMLGenerRangeElement el = this.mainElement;
                while (stack.Count > 0)
                {
                    el.childsTotalCalculated = false;
                    el = stack.Pop();
                    el.sumMinChFreq = 0;
                    el.sumMaxChFreq = 0;
                    el.sumMinChCount = 0;
                    el.sumMaxChCount = 0;
                    el.chUsesCount = false;
                    el.chUsesFreq = false;
                    el.sumMinChMixCount = 0;
                    el.sumMaxChMixCount = 0;
                    foreach (XMLGenerRangeElement chNode in el.childNodes)
                    {
                        if (chNode == null)
                            continue;
                        if (chNode.useFrequency)
                        {
                            el.chUsesFreq = true;
                            el.sumMinChFreq += chNode.minFrequency;
                            el.sumMaxChFreq += chNode.maxFrequency;
                        }
                        else
                        {
                            el.chUsesCount = true;
                            el.sumMinChCount += chNode.minCount;
                            el.sumMaxChCount += chNode.maxCount;
                        }
                        stack.Push(chNode);
                    }

                    if (el.chUsesFreq && !el.chUsesCount &&
                        (el.parentNode == null || !((el.parentNode as XMLGenerRangeElement).useFanout)))
                    {
                        logger.addErrorLog(this.GetType().ToString(), "Child elements of node " + el.name + " are using frequencies, bud node has not set fanout or one at least one child node has to have set min/max count.");
                        logger.addErrorHintLog(this.GetType().ToString(), " Set fanout in element " + el.name + " or set min/max count in some child element of element " + el.name + ".");
                        return false;
                    }
                    //if sum of childs min frequencies if greather than 100% = nonsense
                    if (el.sumMinChFreq > 100)
                    {
                        logger.addErrorLog(this.GetType().ToString(), "In node " + el.name + " is sum of child minimal frequency greather than 100%.");
                        logger.addErrorHintLog(this.GetType().ToString(), "Reduce sum of minimal frequency in child nodes to 100% maximaly. Current value is" + Convert.ToString(el.sumMinChFreq) + "% .");
                        return false;
                    }

                    //sum of min freq = 100 -> sum of mincount = 0
                    if (el.sumMinChFreq == 100 && el.sumMinChCount > 0)
                    {
                        logger.addErrorLog(this.GetType().ToString(), "In node " + el.name + " is sum of child minimal frequency 100 and sum of minimal child count greather than 0.");
                        logger.addErrorHintLog(this.GetType().ToString(), "Reduce sum of minimal frequency in child elements or set nimimal count of child elments to 0.");
                        return false;
                    }

                    el.sumMinChMixCount = Math.Round(el.sumMinChCount + (el.sumMinChCount * el.sumMinChFreq) / (100 - el.sumMinChFreq)); ;
                    if (el.useFanout)
                    {
                        if (el.maxFanout < el.sumMinChMixCount)
                        {
                            logger.addErrorLog(this.GetType().ToString(), "Maximal fanout of element " + el.name + " is lower than minimal sum of child elements.");
                            logger.addErrorHintLog(this.GetType().ToString(), "Reduce sum of minimal frequencies or minimal count in child elements.");
                            return false;
                        }
                        if (el.sumMinChMixCount > el.minFanout)
                        {
                            logger.addErrorLog(this.GetType().ToString(), "Minimal fanout of elemen " + el.name + " is lower than minimal sum of child elements.");
                            logger.addErrorHintLog(this.GetType().ToString(), "Reduce sum of minimal frequencies or minimal count in child elements.");
                            return false;
                        }
                    } // else = ok

                    if (el.sumMaxChFreq < 100)
                    {
                        el.sumMaxChMixCount = Math.Round(el.sumMaxChCount + (el.sumMaxChCount * el.sumMaxChFreq) / (100 - el.sumMaxChFreq));
                        if (el.sumMaxChMixCount < el.minFanout && el.useFanout)
                        {
                            if (el.childNodes.Count == 0)
                            {
                                logger.addErrorLog(this.GetType().ToString(), "Minimal fanout of element " + el.name + " is set, but not child elements are defined.");
                                logger.addErrorHintLog(this.GetType().ToString(), "Disable fanout or add some child elements.");
                                return false;
                            }
                            else
                            {
                                logger.addErrorLog(this.GetType().ToString(), "Minimal fanout of element " + el.name + " is greather than maximal sum of child elements.");
                                logger.addErrorHintLog(this.GetType().ToString(), "Increase sum of minimal frequencies or maximal count in child elements.");
                                return false;
                            }
                        }
                    }
                    else
                    {
                        if (!el.useFanout)
                        {
                            logger.addErrorLog(this.GetType().ToString(), "Maximal count of sub elements is not restricted. Reason: Sum of max frequencies under element " + el.name + " is greather than 100%.");
                            logger.addErrorHintLog(this.GetType().ToString(), "Set parents element fanout to set maximal count of subelements.");
                            return false;
                        }
                    }

                    if (el.useFanout)
                    {
                        el.sumMinChMixCount = el.minFanout;
                        el.sumMaxChMixCount = el.maxFanout;
                    }
                }
            }
            catch (OverflowException)
            {
                this.logger.addErrorLog(this.GetType().ToString(), "Stack overflow: number has more than 28 digits.");
                logger.addErrorHintLog(this.GetType().ToString(), "Decrease maximal count or fanout in some elements.");
                return false;
            }
            return true;
        }

        private void calculateTotalCount(XMLGenerRangeElement el)
        {
            el.minTotalElemens = 1;
            el.maxTotalElemens = 1;
            if (el.childNodes.Count == 0)
            {
                return;
            }
            decimal minCountLeft = 0;
            decimal maxCountLeft = 0;
            // set maximum and minimum count of child elements
            if (el.useFanout)
            {
                minCountLeft = el.minFanout;
                maxCountLeft = el.maxFanout;
            }
            else
            {
                minCountLeft = el.sumMinChMixCount;
                maxCountLeft = el.sumMaxChMixCount;
            }
            XMLGeneratingItem.calculateMinMaxElements(el, minCountLeft, maxCountLeft);
        }

        #endregion

        #region Generating - preparing : modules,checking

        private bool initModules()
        {
            Queue<XMLGenerNode> xqueue = new Queue<XMLGenerNode>();
            xqueue.Enqueue(this.mainElement);
            while (xqueue.Count > 0)
            {
                XMLGenerNode n = xqueue.Dequeue();
                if (n == null) 
                    continue;
                List<XMLGenerNode> childs = n.allChildNodes;
                //xqueue.Concat(childs);
                foreach (XMLGenerNode childNode in childs)
                {
                    xqueue.Enqueue(childNode);
                }
                if (!n.initModule())
                    return false;
            }
            return true;


        }



        /// <summary>
        /// Variable for writing output.
        /// </summary>
        private XMLOutputGenerator outPut = null;
        
        /// <summary>
        /// This variable is used as blocker for generating unchecked 
        /// scheme.(It blocks generating if it is set to false)
        /// </summary>
        private bool checkWasSuccessful = false;
        List<decimal> documentCountToGenerate = new List<decimal>();

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public bool check()
        {
            if (this.isNew)
            {
                this.logger.addErrorLog(this.GetType().ToString(), "Document must be saved before generating.");
                return false;
            }
            return this.check(true);
        }

        /// <summary>
        /// Check scheme if it is valid.
        /// </summary>
        /// <returns></returns>
        public bool check(bool recursive)
        {
            
            this.checkWasSuccessful = false;
            if (recursive)
            {
                foreach (XMLGenerDocument preDoc in getAllRecursivePregeneratedDocuments().Values)
                {
                    if (!preDoc.check(false))
                        return false;
                    
                }
            }

            if (!preCalculate()) return false;
            
            XMLGeneratingItem.rnd = new Random(this.randomInit);
            decimal reserve = this.mainElement.maxTotalElemens - this.mainElement.minTotalElemens;
            documentCountToGenerate.Clear();
            
            totalSum = 0;
            decimal rndDocCount;
            for (int i = 0; i < this.documentsCount; i++)
            { 
                rndDocCount = this.mainElement.minTotalElemens + Math.Round(0.5m + reserve * Convert.ToDecimal(XMLGeneratingItem.rnd.NextDouble()));
                documentCountToGenerate.Add(rndDocCount);
                this.totalSum += rndDocCount;
            }
            documentsLeft = this.documentsCount;
            //this.totalSum = this.mainElement.minTotalElemens + Math.Round(0.5m + reserve * Convert.ToDecimal(XMLGeneratingItem.rnd.NextDouble()));
            
            outPut = new XMLOutputGenerator(this);
            this.checkWasSuccessful = true;
            return true;

        }

        #endregion

        #region Generating - core

        /// <summary>
        /// How many document should be generated
        /// </summary>
        public int documentsLeft = 1;
        
        /// <summary>
        /// How many elements was generated
        /// </summary>
        public decimal generatedElements = 0;
        
        /// <summary>
        /// Control variable for stoping generation process
        /// </summary>
        public bool stopGenerating = false;

        // time stops
        private Stopwatch timeCalc = new Stopwatch();
        private Stopwatch timeGetNext = new Stopwatch();
        private Stopwatch timeOut = new Stopwatch();
        private Stopwatch timeOutModule = new Stopwatch();

        /// <summary>
        /// 
        /// </summary>
        public void generate()
        {
            generate(false);
        }

        /// <summary>
        /// generate xml document
        /// </summary>
        public void generate(bool recursive)
        {
            //generate previous documents
            if (recursive)
            {
                foreach (XMLGenerDocument preDoc in sortedPreGeneratingDocuments)
                {
                    if (preDoc.generateDependenyDepth > 0)
                        preDoc.generate();
                }
            }
            timeCalc = new Stopwatch();
            timeGetNext = new Stopwatch();
            timeOut = new Stopwatch();
            timeOutModule = new Stopwatch();
            //StringBuilder sb = new StringBuilder();

            // set variable for corrupting loop to false
            stopGenerating = false;
            // set generated elements count 
            generatedElements = 0;

            if (!this.checkWasSuccessful)
            {
                this.logger.addErrorLog(this.GetType().ToString(),"Checking result was not successful. Output document can not be generated.");
                return ;
            }
            // generte DTD/XSD
            generateDefinitionDocument();
            if (startGeneratingEvent!=null)
                startGeneratingEvent(new XLogMessage(this.GetType().ToString(),"Generating starts",XLogMessage.LogType.LOG));
            // generte all documents
            timeOut.Start();
            for (int i = 0; i < this.documentsCount  && !stopGenerating; i++ )
            {
                generateDocument(i);
                //timeOut.Stop(); timeOutModule.Start();
                if (!useOutputModule(i,recursive))
                    stopGenerating = true;
                //timeOut.Start(); timeOutModule.Stop();
                documentsLeft--;
            }

            // show results
            logger.addLog(this.GetType().ToString(), "---------------------- " );
            if (stopGenerating)
                logger.addLog(this.GetType().ToString(), "Generating canceled.");
            else
                logger.addLog(this.GetType().ToString(), "Generating done.");
            timeOut.Stop();
            logger.addLog(this.GetType().ToString(), "Generated elements total : " + Convert.ToString(this.generatedElements) + ".");

            TimeSpan tsCalc = timeCalc.Elapsed;
            string calculatingTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}",
                tsCalc.Hours, tsCalc.Minutes, tsCalc.Seconds,
                tsCalc.Milliseconds / 10);
            TimeSpan tsNext = timeGetNext.Elapsed;
            string nextTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}",
                tsNext.Hours, tsNext.Minutes, tsNext.Seconds,
                tsNext.Milliseconds / 10);

            TimeSpan tsOut = timeOut.Elapsed;
            string outTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}",
                tsOut.Hours, tsOut.Minutes, tsOut.Seconds,
                tsOut.Milliseconds / 10);
            TimeSpan tsOutMod = timeOutModule.Elapsed;
            string outModTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}",
                tsOutMod.Hours, tsOutMod.Minutes, tsOutMod.Seconds,
                tsOutMod.Milliseconds / 10);

            logger.addLog(this.GetType().ToString(), "Spent on calculating: " + calculatingTime);
            logger.addLog(this.GetType().ToString(), "Spent on getNext: " + nextTime);
            logger.addLog(this.GetType().ToString(), "Spent on other: " + outTime);
            logger.addLog(this.GetType().ToString(), "Spent on outputModule: " + outModTime);

            
        
        }

        /// <summary>
        /// This method uses output module for temporarly gnerated document
        /// </summary>
        /// <param name="index">Index of generted document</param>
        /// <returns>false if exception happend during process, otherwise true</returns>
        private bool useOutputModule(int index, bool useSecondModule)
        {
            // no output module
            string filenameIn = outFileFullPathIndex(index, true);
            string filenameOut = outFileFullPathIndex(index, false);

            if (this.module != "none")
            {
                filenameIn = outFileFullPathIndex(index, true);
                filenameOut = outFileFullPathIndex(index, false);
                try
                {
                    XMLGenerOutputInterface outMod = XModuleManager.getOutputModuleInstance(this.module);
                    if (outMod == null)
                    {
                        logger.addErrorLog(this.GetType().ToString(), "Creating instance of module " + this.module + " failed.");
                        return false;
                    }
                    outMod.setParameters(this.outputElem.OuterXml);
                    outMod.generateOutput(filenameIn, filenameOut);
                }
                catch (Exception e)
                {
                    logger.addErrorLog(this.GetType().ToString(), "Output module (" + this.module + ") error: " + e.Message);
                    return false;
                }
            }

            if (useSecondModule)
            {
                string file = filenameIn;
                if (this.module != "none")
                    file = filenameOut;
                if (secondModule == "none")
                    return true;

                try
                 {
                    XMLGenerOutputInterface secondOutMod = XModuleManager.getOutputModuleInstance(this.secondModule);
                    if (secondOutMod == null)
                    {
                        logger.addErrorLog(this.GetType().ToString(), "Creating instance of module " + this.secondModule + " failed.");
                        return false;
                    }
                    secondOutMod.setParameters(this.secondOutputElem.xmlElem.OuterXml);
                    secondOutMod.processOutput(file);
                }
                catch (Exception e)
                {
                    logger.addErrorLog(this.GetType().ToString(), "Output module (" + this.secondModule + ") error: " + e.Message);
                    return false;
                }
            
            }
            return true;
        }

        /// <summary>
        /// Generate xml document
        /// </summary>
        /// <param name="index">document index</param>
        private void generateDocument(int index)
        {
            // open xml document file
            if (!outPut.openFile(index)) return ;
            // initialize document
            if(!initModules() )
            {
                this.outPut.close();
                return;
            };

            this.checkWasSuccessful = false;
            Stack<XMLGeneratingItem> xstack = new Stack<XMLGeneratingItem>();
            //this.mainElement.minCountForGenarating = 1;
            XMLGeneratingItem startElement = new XMLGeneratingItem(this.mainElement, this.outPut);

            startElement.generateTotal = documentCountToGenerate[index];
            String info =  String.Format("Generatting {0}.document, generating {1} elements.", index + 1, documentCountToGenerate[index]);
            logger.addLog(this.GetType().ToString(), info);

            xstack.Push(startElement);
            startElement.waitingForChilds = false;

            XMLGeneratingItem currentItem = null;
            XMLGeneratingItem nextItem = null;
            decimal generatedThisElements = 0;
            
            this.outPut.writeLine("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>");
            bool insertHeaderAsAttribute = true;
            if (this.xmlDocConverter!=null && !this.xmlDocConverter.schemaHeaderIsDocumentAttribute())
            {
                this.outPut.writeLine(this.xmlDocConverter.getSchemaHeaderForXML());
                insertHeaderAsAttribute = false;
            }
            if (this.xmlDocConverter == null)
            {
                insertHeaderAsAttribute = false;
            }

            while (xstack.Count > 0 && !stopGenerating)
            {
                currentItem = xstack.Peek();

                // case when method parentItem.getNextSubElement() 
                //  returns null = no next child element
                if (currentItem == null)
                {
                    xstack.Pop();
                    continue;
                }
                if (!currentItem.waitingForChilds)
                {
                    //this element was just added
                    currentItem.printElementsHeader();
                    if (insertHeaderAsAttribute)
                    {
                        insertHeaderAsAttribute = false;
                        this.outPut.write(" " + this.xmlDocConverter.getSchemaHeaderForXML());
                    }
                    generatedElements++;
                    generatedThisElements++;
                    //timeCalc.Start(); timeOut.Stop();
                    currentItem.calculateChildItems();
                    //timeCalc.Stop(); timeOut.Start();
                    //if (currentItem.childElementsLeft>0)
                    //  logger.addLog(this.GetType().ToString(), "Log: "+currentItem.sourceNode.name+",  childs:" +Convert.ToString(currentItem.childElementsLeft)+"  ,current count:" + Convert.ToString(this.generatedElements) + " .");

                    //timeOut.Start();
                    if (currentItem.childElementsLeft > 0)
                    {
                        currentItem.closeHeaderForChildren();
                        currentItem.haschilds = true;
                        currentItem.waitingForChilds = true;
                        //timeGetNext.Start(); timeOut.Stop();
                        nextItem = currentItem.getNextSubElement();
                        //timeGetNext.Stop(); timeOut.Start();
                        if (nextItem != null)
                        {
                            nextItem.waitingForChilds = false;
                            xstack.Push(nextItem);
                        }
                    }
                    else
                    {
                        currentItem.haschilds = false;
                        currentItem.waitingForChilds = false;
                        currentItem.printElementsEnd();
                        xstack.Pop();

                    }

                    //timeOut.Stop();

                }
                else
                {
                    if (currentItem.childElementsLeft > 0)
                    {

                        //timeGetNext.Start(); timeOut.Stop();
                        nextItem = currentItem.getNextSubElement();
                        //timeGetNext.Stop(); timeOut.Start();
                        if (nextItem != null)
                        {
                            nextItem.waitingForChilds = false;
                            xstack.Push(nextItem);
                        }
                    }
                    else
                    {
                        currentItem.waitingForChilds = false;
                        currentItem.printElementsEnd();
                        xstack.Pop();
                    }

                }

            }
            this.outPut.close();
            this.printStatistic(startElement);
            if (!stopGenerating)
            {
                info = String.Format("Generatting {0}. document done. Generated {1} elements.", index + 1, generatedThisElements);
                logger.addLog(this.GetType().ToString(), info);
            }


        }

        #endregion

        #region Generating - document scheme

        //------------------------------------------
        // generate scheme document
        //------------------------------------------
        /// <summary>
        /// Instance of definition document converter
        /// </summary>
        private XMLSchemeConverter xmlDocConverter = null;

        /// <summary>
        /// Function generates definition document
        /// </summary>
        public void generateDefinitionDocument()
        {
            if (this.outSchemeType == "xsd")
            {
                xmlDocConverter= new XMLGenerXSDConverter(this.logger);
            }
            else if(this.outSchemeType == "dtd")
            {
                xmlDocConverter = new XMLGenerDTDConverter(this.logger);
            }
            if (xmlDocConverter == null)
            {
                //logger.addErrorLog(this.GetType().ToString(), "Reference to converter is null for scheme type " + this.outSchemeType);
                return;
            }
            xmlDocConverter.generateScheme(this);

        }

        #endregion

        #region dependency comparing

        /// <summary>
        /// Instance of pre-generating document compo
        /// </summary>
        private static DocumentDependencyComparer docDependencyComparer = new DocumentDependencyComparer();

        /// <summary>
        /// Comparer used in sorting of coduments, 
        /// which should be generated before this document
        /// </summary>
        private class DocumentDependencyComparer : IComparer<XMLGenerDocument>
        {

            
            /// <summary>
            /// Document generating compared - compare depth of dependency
            /// </summary>
            /// <param name="x">document</param>
            /// <param name="y">document</param>
            /// <returns>see int.CompereTo(x,y)</returns>
            public int Compare(XMLGenerDocument x, XMLGenerDocument y)
            {
                return y.generateDependenyDepth.CompareTo(x.generateDependenyDepth);
            }
        }

        #endregion

        #region statictics

        private void printStatistic(XMLGeneratingItem startElement)
        { 
            TextWriter tw = new StreamWriter("stat.txt");
            Stack<XMLGeneratingItem> xstack = new Stack<XMLGeneratingItem>();
            xstack.Push(startElement);
            startElement.waitingForChilds = false;

            XMLGeneratingItem currentItem = null;



            StringBuilder sb = new StringBuilder();
            int i = 0;
            XMLGeneratingItem childItem =null;
            while (xstack.Count > 0 )
            {
                currentItem = xstack.Pop();
                sb.Length = 0;
                sb.AppendFormat("{0,20} {1}",currentItem.sourceNode.name,currentItem.generatedCount);
                tw.WriteLine(sb.ToString());
                if (currentItem.childs != null)
                    for (i = currentItem.childs.Count - 1; i >= 0;i-- )
                    {
                        childItem = currentItem.childs[i];
                        xstack.Push(childItem);
                    }
            }
            
            tw.Close();
        }
        #endregion



    }

    
}
